package com.yonyou.brigade.order_center.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.yonyou.brigade.order_center.dao.InvoiceRelationshipMapper;
import com.yonyou.brigade.order_center.model.*;
import com.yonyou.brigade.order_center.service.*;
import com.yonyou.ybzboot.starter.common.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * 发票订单关联表 服务实现类
 * </p>
 *
 * @author houyb
 * @since 2020-03-03
 */
@Service
public class InvoiceRelationshipServiceImpl extends ServiceImpl<InvoiceRelationshipMapper, InvoiceRelationship> implements IInvoiceRelationshipService {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IBriFlightInfoService briFlightInfoService;

    @Autowired
    private IBriFlightOrderService briFlightOrderService;

    @Autowired
    private IBriCarOrderService briCarOrderService;

    @Autowired
    private IBriTrainTicketService briTrainTicketService;

    @Autowired
    private IBriTrainOrderService trainOrderService;

    @Autowired
    private IBriHotelOrderService briHotelOrderService;


    @Override
    public boolean manualMatching(String action, JSONArray invoicesBrigades) {
        List<InvoiceRelationship> matchList = new ArrayList<>();
        List<InvoiceRelationship> cancelList = new ArrayList<>();
        for (int i = 0; i < invoicesBrigades.size(); i++) {
            JSONObject invoice = invoicesBrigades.getJSONObject(i);
            Wrapper<InvoiceRelationship> wrapper = new EntityWrapper<>();

            String orderClassification = invoice.getString("orderClassification");
            String orderNo = invoice.getString("orderNo");
            String billType = invoice.getString("billType");
            String invoiceCode = invoice.getString("invoiceCode");
            String invoiceNum = invoice.getString("invoiceNum");
            wrapper.eq("order_no", orderNo);
            wrapper.eq("order_classification", orderClassification);
            wrapper.eq("invoice_type", billType);
            wrapper.eq("invoice_num", invoiceNum);
            if ("cancel".equals(action)){
                wrapper.eq("valid", 1);
            }
            if (invoiceCode == null){
                wrapper.isNull("invoice_code");
            }else {
                wrapper.eq("invoice_code", invoiceCode);
            }
            List<InvoiceRelationship> relationshipList = this.selectList(wrapper);

            //如果能查到，放入取消匹配list
            if (relationshipList.size() > 0){
                for (InvoiceRelationship relationship : relationshipList) {
                    if ("cancel".equals(action)){
                        relationship.setValid(0);
                        cancelList.add(relationship);
                    }else {
                        relationship.setValid(1);
                        matchList.add(relationship);
                    }
                }
            }else{
                InvoiceRelationship relationship = new InvoiceRelationship();
                relationship.setInvoiceCode(invoiceCode);
                relationship.setInvoiceNum(invoiceNum);
                relationship.setInvoiceType(billType);
                relationship.setOrderClassification(orderClassification);
                relationship.setOrderNo(orderNo);
                relationship.setValid(1);
                matchList.add(relationship);
            }
        }

        if ("cancel".equals(action)){
            if (cancelList.size() < 1){
                return true;
            }
            return this.updateBatchById(cancelList);
        }else {
            return this.insertOrUpdateBatch(matchList);
        }
    }

    @Override
    public JSONObject invoiceMatchOrder(String tenantId, String userId, JSONObject matchData) {
        JSONArray invoiceArray = matchData.getJSONArray("invoiceArray");
        JSONArray orderArray = matchData.getJSONArray("orders");
        JSONArray mismatchedOrderArray = new JSONArray();

        for (int i = 0; i < orderArray.size(); i++) {
            JSONObject orderJson = orderArray.getJSONObject(i);
            String orderNo = orderJson.getString("orderNo");
            String orderType = orderJson.getString("orderClassification");
            if (orderType == null) {
                logger.error("订单类型为空：" + orderJson.toJSONString());
                continue;
            }
            boolean matched = false;
            logger.info("开始匹配 " + orderType + " 发票和订单");
            switch (orderType){
                case "flight":
                    matched = flightInvoiceMatch(invoiceArray, orderNo, orderType, tenantId);
                    break;
                case "hotel":
                    matched = hotelInvoiceMatch(invoiceArray, orderNo, orderType, tenantId);
                    break;
                case "train":
                    matched = trainInvoiceMatch(invoiceArray, orderNo, orderType, tenantId);
                    break;
                case "car":
                    matched = carInvoiceMatch(invoiceArray, orderNo, orderType, tenantId);
                    break;
            }
            //记录不匹配的订单
            if (!matched){
                logger.info("订单未能匹配发票>>>" + orderJson.toJSONString());
                JSONObject order = new JSONObject();
                order.put("orderNo", orderNo);
                order.put("orderClassification", orderType);
                mismatchedOrderArray.add(order);
            }
        }

        //将匹配过的发票和为匹配过的区分开
        List<Map<String,Object>> matchedInvoiceArr = new ArrayList<>();
        List<Map<String,Object>> summaryInvoiceArr = new ArrayList<>();
        for (int i = 0; i < invoiceArray.size(); i++) {
            JSONObject invoice = invoiceArray.getJSONObject(i);
            String invoiceCode = invoice.getString("invoiceCode");
            String invoiceType = invoice.getString("billType");
            String invoiceNum = invoice.getString("invoiceNum");
            JSONArray orderArr = invoice.getJSONArray("orderArray");
            Map<String,Object> rsInvoice = new HashMap<>();
            rsInvoice.put("invoiceCode", invoiceCode);
            rsInvoice.put("billType", invoiceType);
            rsInvoice.put("invoiceNum", invoiceNum);
            if (orderArr != null) {
                rsInvoice.put("orderArray", orderArr);
                matchedInvoiceArr.add(rsInvoice);
            }else {
                summaryInvoiceArr.add(rsInvoice);
            }
        }
        //将转换好的记事返回
        JSONObject result = new JSONObject();
        result.put("orders", mismatchedOrderArray);
        result.put("summaryArray",summaryInvoiceArr);
        result.put("matchArray", matchedInvoiceArr);
        return result;
    }

    @Override
    public List<InvoiceRelationship> getRelationshipsByOrderNo(String orderNo) {
        Wrapper<InvoiceRelationship> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        wrapper.eq("valid", 1);
        return this.selectList(wrapper);
    }

    /**
     * 机票匹配发票和订单
     * @param invoiceArray
     * @param orderNo
     * @param tenantId
     * @return
     */
    private boolean flightInvoiceMatch(JSONArray invoiceArray, String orderNo, String orderType, String tenantId){

        //查询订单的航程信息
        Wrapper<BriFlightInfo> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        List<BriFlightInfo> briFlightInfos = briFlightInfoService.selectList(wrapper);
        // 只有单航程的才能匹配，多航程或者没查到航程信息不做匹配
        if (briFlightInfos.size() != 1){
            logger.info("订单不是单航程>>>" + orderNo);
            return false;
        }

        Wrapper<BriFlightOrder> orderWrapper = new EntityWrapper<>();
        orderWrapper.eq("order_no", orderNo);
        List<BriFlightOrder> briFlightOrders = briFlightOrderService.selectList(orderWrapper);
        JSONObject flightOrder = JSONObject.parseObject( JSONObject.toJSONString( briFlightOrders.get(0) ) );

        BriFlightInfo briFlightInfo = briFlightInfos.get(0);
        JSONObject flightJSON = JSONObject.parseObject( JSONObject.toJSONString( briFlightInfo ) );
        return invoiceMatch(tenantId, invoiceArray, flightJSON, BriFlightInfo.class, flightOrder, BriFlightOrder.class, orderType);
    }

    /**
     * 获取关联对象
     * @param order
     * @param invoice
     * @return
     */
    private InvoiceRelationship getRelationship(JSONObject order, JSONObject invoice, String orderType){
        InvoiceRelationship relationship = new InvoiceRelationship();
        relationship.setOrderNo(order.getString("orderNo"));
        relationship.setOrderClassification(orderType);
        relationship.setInvoiceCode(invoice.getString("invoiceCode"));
        relationship.setInvoiceNum(invoice.getString("invoiceNum"));
        relationship.setInvoiceType(invoice.getString("billType"));
        relationship.setValid(1);
        return relationship;
    }

    /**
     * 匹配发票和订单
     * @param tenant
     * @param invoiceArray
     * @param orderDetail
     * @param orderType
     * @param orderInfo
     * @return
     */
    private boolean invoiceMatch(String tenant, JSONArray invoiceArray, JSONObject orderDetail,Class detailClass, JSONObject orderInfo,Class orderClass, String orderType){
        boolean matched = false;
        for (int i = 0; i < invoiceArray.size(); i++) {
            JSONObject invoice = invoiceArray.getJSONObject(i);
            //如果类型不匹配值跳过
            if (!invoice.containsKey(orderType)){
                continue;
            }
            matched = true;
            //根据匹配规则判断发票和订单中的值是否相等
            JSONObject matchData = invoice.getJSONObject(orderType);
            for (String key : matchData.keySet()) {
                JSONObject matchRuleData = invoice.getJSONObject(orderType);
                String invoiceValue = matchRuleData.getString(key);
                String orderDetailValue = orderDetail.getString(key);
                // 日期类型在商旅中会被JOSNObject转成时间戳，所以将参数中的时间字符串转也为时间戳
                BigDecimal invoiceDec = null;
                BigDecimal orderDec = null;

                if (hasField(detailClass,key)) {
                    if (isDateField(detailClass,key)){
                        try {
                            long longValue = unifiedDateValue(invoiceValue);
                            invoiceValue = String.valueOf(longValue);
                        }catch (Exception e){
                            logger.error("匹配时间转换异常："+invoiceValue);
                        }
                    }

                    if (isBigDecimal(detailClass,key)){
                        invoiceDec = new BigDecimal(invoiceValue);
                        orderDec = new BigDecimal(orderDetailValue);
                    }
                }
                if (orderInfo != null && hasField(orderClass, key)){
                    orderDetailValue = orderInfo.getString(key);
                    if (isDateField(orderClass, key)){
                        try{
                            long longValue = unifiedDateValue(invoiceValue);
                            invoiceValue = String.valueOf(longValue);
                        }catch (Exception e){
                            logger.error("匹配时间转换异常"+invoiceValue);
                        }
                    }

                    if (isBigDecimal(orderClass,key)){
                        invoiceDec = new BigDecimal(invoiceValue);
                        orderDec  = new BigDecimal(orderDetailValue);
                    }
                }
                //都为空，判断为相等
                if (StringUtil.isEmpty(invoiceValue) && StringUtil.isEmpty(orderDetailValue)){
                    continue;
                }
                //值不相等，该发票和订单不匹配，直接进行下一张发票的匹配
                if (StringUtil.isNotEmpty(invoiceValue) && !invoiceValue.equals(orderDetailValue)) {
                    if (invoiceDec != null && orderDec != null && invoiceDec.compareTo(orderDec) == 0){
                        continue;
                    }else {
                        matched = false;
                    }
                    break;
                }
            }

            //匹配发票和订单，将订单的信息放到发票内，在数据库做记录
            if (matched){
                JSONArray orderArr = new JSONArray();
                JSONObject order = new JSONObject();
                order.put("orderClassification", orderType);
                order.put("orderNo", orderDetail.getString("orderNo"));
                orderArr.add(order);
                invoiceArray.getJSONObject(i).put("orderArray", orderArr) ;
                InvoiceRelationship relationship = getRelationship(orderDetail, invoice, orderType);
//                relationship.setTenant(tenant);
                //如果订单有匹配记录，或者发票有匹配记录都认定为已关联
                Wrapper<InvoiceRelationship> wrapper = new EntityWrapper<>();
                wrapper.eq("order_no", relationship.getOrderNo());
                wrapper.or();
                wrapper.eq("invoice_type", relationship.getInvoiceType());
                wrapper.eq("invoice_num", relationship.getInvoiceNum());
                List<InvoiceRelationship> invoiceRelationships = this.selectList(wrapper);
                // 如果未关联
                if (invoiceRelationships.size() < 1){
                    this.insert(relationship);
                    logger.info("插入关联关系>>>" + relationship);
                }else {
                    for (InvoiceRelationship rsp : invoiceRelationships) {
                        rsp.setValid(1);
                    }
                    this.updateBatchById(invoiceRelationships);
                    logger.info("更新关联关系>>>" + invoiceRelationships);
                }
                break;
            }
        }

        return matched;
    }

    private boolean isBigDecimal(Class cls, String key) {
        try {
            Field field = cls.getDeclaredField(key);
            if (field.getType() == BigDecimal.class){
                return true;
            }else {
                return false;
            }
        }catch (Exception e){
            return false;
        }
    }

    /**
     * 将时间字符串转成时间戳
     * @param timeString
     * @return
     */
    private long unifiedDateValue(String timeString) throws Exception{
        String strFormat = "HH:mm:ss";
        switch (timeString.length()){
            case 5:
                strFormat = "HH:mm";
                break;
            case 10:
                strFormat = "yyyy-MM-dd";
                break;
            case 19:
                strFormat = "yyyy-MM-dd HH:mm:ss";
                break;
        }
        SimpleDateFormat format = new SimpleDateFormat(strFormat);
        Date parse = format.parse(timeString);
        return parse.getTime();


    }

    /**
     * 判断是否有字段
     * @param cls
     * @param key
     * @return
     */
    private boolean hasField(Class cls, String key){
        try{
            cls.getDeclaredField(key);
        }catch (Exception e){
            return false;
        }
        return true;
    }
    /**
     * 判断对象是否是日期类型
     * @param cls
     * @param key
     * @return
     */
    private boolean isDateField(Class cls, String key) {
        try {
            Field field = cls.getDeclaredField(key);
            if (field.getType() == Date.class){
                return true;
            }else {
                return false;
            }
        }catch (Exception e){
            return false;
        }
    }

    /**
     * 酒店匹配发票和订单
     * @param invoiceArray
     * @param orderNo
     * @param orderType
     * @param tenantId
     * @return
     */
    private boolean hotelInvoiceMatch(JSONArray invoiceArray, String orderNo, String orderType, String tenantId){
        //查询酒店订单信息
        Wrapper<BriHotelOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        List<BriHotelOrder> briHotelOrders = briHotelOrderService.selectList(wrapper);
        // 只有单航程的才能匹配，多航程或者没查到航程信息不做匹配
        if (briHotelOrders.size() != 1){
            return false;
        }
        JSONObject hotelOrder = JSONObject.parseObject( JSONObject.toJSONString( briHotelOrders.get(0) ) );

        return invoiceMatch(tenantId, invoiceArray, hotelOrder,BriHotelOrder.class, null, null, orderType);
    }

    /**
     * 火车匹配发票和订单
     * @param invoiceArray
     * @param orderNo
     * @param orderType
     * @param tenantId
     * @return
     */
    private boolean trainInvoiceMatch(JSONArray invoiceArray, String orderNo, String orderType, String tenantId){
        //查询订单的火车票信息
        Wrapper<BriTrainTicket> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        List<BriTrainTicket> briTrainTickets = briTrainTicketService.selectList(wrapper);
        // 只有单航程的才能匹配，多航程或者没查到航程信息不做匹配
        if (briTrainTickets.size() != 1){
            return false;
        }
        JSONObject trainTicket = JSONObject.parseObject( JSONObject.toJSONString( briTrainTickets.get(0) ) );
        //查询订单的火车票信息
        Wrapper<BriTrainOrder> orderWrapper = new EntityWrapper<>();
        orderWrapper.eq("order_no", orderNo);
        List<BriTrainOrder> trainOrders = trainOrderService.selectList(orderWrapper);
        JSONObject trainOrder = JSONObject.parseObject( JSONObject.toJSONString( trainOrders.get(0) ) );

        return invoiceMatch(tenantId, invoiceArray, trainTicket, BriTrainTicket.class, trainOrder, BriTrainOrder.class, orderType);
    }

    /**
     * 用车匹配发票和订单
     * @param invoiceArray
     * @param orderNo
     * @param orderType
     * @param tenantId
     * @return
     */
    private boolean carInvoiceMatch(JSONArray invoiceArray, String orderNo, String orderType, String tenantId){
        //查询用车订单的信息
        Wrapper<BriCarOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        List<BriCarOrder> briCarOrders = briCarOrderService.selectList(wrapper);
        // 只有单航程的才能匹配，多航程或者没查到航程信息不做匹配
        if (briCarOrders.size() != 1){
            return false;
        }

        JSONObject carOrder = JSONObject.parseObject( JSONObject.toJSONString( briCarOrders.get(0) ) );
        return invoiceMatch(tenantId, invoiceArray, carOrder, BriCarOrder.class, null, null, orderType);
    }

}
